home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / X11 / xconq / xconq.c < prev   
C/C++ Source or Header  |  1995-05-09  |  19KB  |  713 lines

  1. /* Copyright (c) 1987, 1988  Stanley T. Shebs, University of Utah. */
  2. /* This program may be used, copied, modified, and redistributed freely */
  3. /* for noncommercial purposes, so long as this notice remains intact. */
  4.  
  5. #pragma comment(exestr, "@(#) xconq.c 12.1 95/05/09 ")
  6.  
  7. /* RCS $Header: xconq.c,v 1.4 88/07/17 17:11:08 shebs Exp $ */
  8.  
  9. /* Unlike most multi-player network programs, xconq does not use multiple */
  10. /* processes.  Instead, it relies on the network capability inherent in  */
  11. /* some graphical interfaces (such as X) to open up multiple displays. */
  12.  
  13. #include "config.h"
  14. #include "misc.h"
  15. #include "period.h"
  16. #include "side.h"
  17. #include "unit.h"
  18. #include "map.h"
  19. #include "global.h"
  20. #include "version.h"
  21.  
  22. /* This is for extremely simple menus to be used with novice players. */
  23.  
  24. typedef struct menu_entry {
  25.     char *name;
  26.     char *description;
  27. } MenuEntry;
  28.  
  29. extern int nummaps;     /* needed to initialize number of maps read */
  30.  
  31. /* Definitions of globally global variables. */
  32.  
  33. Global global;          /* important (i.e. saved/restored) globals */
  34.  
  35. MenuEntry mapmenu[MAXMAPMENU];  /* list of mapfiles to suggest to players */
  36.  
  37. bool givenseen = FALSE; /* true if world known */
  38. bool Debug = FALSE;     /* the debugging flag itself. */
  39. bool Build = FALSE;     /* magic flag for scenario-builders */
  40. bool Freeze = FALSE;    /* keeps machine players from moving */
  41. bool humans[MAXSIDES];  /* flags for which players are human or machine */
  42.  
  43. char *programname;      /* name of the main program */
  44. char *rawfilenames[MAXLOADED];  /* names specified on cmd line */
  45. char version[] = VERSION;  /* version string */
  46. char *hosts[MAXSIDES];  /* names of displays for each side */
  47. char spbuf[BUFSIZE];    /* main printing buffer */
  48. char tmpbuf[BUFSIZE];   /* a temp buffer */
  49.  
  50. int numfiles = 0;       /* number of data files asked for */
  51. int numgivens = 0;      /* number of sides given on cmd line */
  52. int numhumans = 0;      /* number of bona-fide human players */
  53. int givenwidth = DEFAULTWIDTH;    /* requested width for a random map */
  54. int givenheight = DEFAULTHEIGHT;  /* requested height for a random map */
  55. int giventime = 0;      /* requested time for individual turns */
  56.  
  57. /* Indices of groups of mapfile types mentioned in collection of mapfiles. */
  58.  
  59. int scnstart, scnend, perstart, perend, mapstart, mapend;
  60.  
  61. Side *winner = NULL;    /* the winning side (allies are also winners) */
  62.  
  63. /* Where it all begins... the main program's primary duty is command line */
  64. /* interpretation, it hands off for everything else. */
  65.  
  66. main(argc, argv)
  67. int argc;
  68. char *argv[];
  69. {
  70.     bool eopt = FALSE, ask = FALSE;
  71.     char ch;
  72.     int i, numenemies;
  73.  
  74.     programname = argv[0];
  75.     nummaps = 0;
  76.     for (i = 0; i < MAXLOADED; ++i) rawfilenames[i] = "";
  77.     add_default_player();
  78.     for (i = 1; i < argc; ++i) {
  79.     if ((argv[i])[0] == '-') {
  80.         ch = (argv[i])[1];
  81.         if (Debug) printf("-%c\n", ch);
  82.         switch (ch) {
  83.         case 'A':
  84.         if (i+1 >= argc) usage_error();
  85.         add_player(FALSE, argv[++i]);
  86.         break;
  87.         case 'B':
  88.         Build = TRUE;
  89.         Freeze = TRUE;
  90.         break;
  91.         case 'D':
  92.         Debug = TRUE;
  93.         break;
  94.         case 'e':
  95.         if (i+1 >= argc) usage_error();
  96.         eopt = TRUE;
  97.         numenemies = atoi(argv[++i]);
  98.         while (numenemies-- > 0) add_player(FALSE, NULL);
  99.         break;
  100.         case 'm':
  101.         if (i+1 >= argc) usage_error();
  102.         make_pathname(NULL, argv[++i], "map", spbuf);
  103.         rawfilenames[numfiles++] = copy_string(spbuf);
  104.         break;
  105.         case 'M':    
  106.         if (i+2 >= argc) usage_error();
  107.         givenwidth = atoi(argv[++i]);
  108.         givenheight = atoi(argv[++i]);
  109.         break;
  110.         case 'p':
  111.         if (i+1 >= argc) usage_error();
  112.         make_pathname(NULL, argv[++i], "per", spbuf);
  113.         rawfilenames[numfiles++] = copy_string(spbuf);
  114.         break;
  115.         case 'r':
  116.         if (numgivens > 1) {
  117.             fprintf(stderr, "Warning: -r is resetting the list of\n");
  118.             fprintf(stderr, "players already given in the command.\n");
  119.         }
  120.         numgivens = numhumans = 0;
  121.         break;
  122.         case 's':
  123.         if (i+1 >= argc) usage_error();
  124.         make_pathname(NULL, argv[++i], "scn", spbuf);
  125.         rawfilenames[numfiles++] = copy_string(spbuf);
  126.         break;
  127.         case 't':
  128.         if (i+1 >= argc) usage_error();
  129.         /* Converting to seconds for internal use */
  130.         giventime = 60 * atoi(argv[++i]);
  131.         break;
  132.         case 'v':
  133.         givenseen = TRUE;
  134.         break;
  135.         case 'x':
  136.         ask = TRUE;
  137.         break;
  138.         default:
  139.         usage_error();
  140.         }
  141.     } else {
  142.         /* We just have a host name for a human player */
  143.         add_player(TRUE, argv[i]);
  144.     }
  145.     }
  146.     /* Put in a single machine opponent if no -e and no human opponents */
  147.     if (!eopt && numgivens == 1) add_player(FALSE, NULL);
  148.     /* Say hi to the user */
  149.     printf("\n              Welcome to XCONQ version %s\n\n", version);
  150.     maybe_dump_news();
  151.     /* If no command-line options supplied, work interactively */
  152.     if (ask) {
  153.     if (read_menu_file()) {
  154.         ask_about_mapfiles();
  155.     } else {
  156.         printf("No period/map/scenario menus available.\n");
  157.     }
  158.     ask_about_players();
  159.     }
  160.     /* While away the hours setting everything up */
  161.     init_random(-1);
  162.     init_sides();
  163.     init_units();
  164.     init_game();
  165.     init_movement();
  166.     init_displays();
  167.     init_mplayers();
  168.     init_sighandlers();
  169.     /* Relatively low chance of screwup now, so safe to delete saved game */
  170.     if (saved_game()) remove_saved_game();
  171.     /* Play! */
  172.     play();
  173. }
  174.  
  175. /* Complain and leave if command is garbage. */
  176.  
  177. usage_error()
  178. {
  179.     fprintf(stderr, "Usage: %s [display] [-A display] [-B] [-e n] [-m name]\n",
  180.         programname);
  181.     fprintf(stderr, "  [-M w h] [-p name] [-r] [-s name] [-t n] [-v] [-x]\n");
  182.     exit(1);
  183. }
  184.  
  185. /* Add a player into the array of players, making sure of no overflow. */
  186. /* Fail if so, players probably made a typo on command line - help them */
  187. /* out with a list of what players had been included. */
  188.  
  189. add_player(ahuman, host)
  190. bool ahuman;
  191. char *host;
  192. {
  193.     if (numgivens >= MAXSIDES) {
  194.     fprintf(stderr, "At most %d player sides allowed!\n", MAXSIDES);
  195.     fprintf(stderr, "(Valid ones were ");
  196.     list_players(stderr);
  197.     fprintf(stderr, ")\n");
  198.     exit(1);
  199.     }
  200.     hosts[numgivens] = host;
  201.     humans[numgivens] = ahuman;
  202.     numgivens++;
  203.     if (ahuman) numhumans++;
  204.     if (Debug) printf("Added %s player (%s)\n",
  205.               (ahuman ? "human" : "machine"), (host ? host : ""));
  206. }
  207.  
  208. /* The main loop that cycles through the turns and the phases. */
  209. /* Always departs through game end phase (or by core dump :-( ). */
  210.  
  211. play()
  212. {
  213.     while (TRUE) {
  214.     init_turn_phase();
  215.     spy_phase();
  216.     disaster_phase();
  217.     build_phase();
  218.     flush_dead_units();
  219.     supply_phase();
  220.     sort_units(FALSE);
  221.     movement_phase();
  222.     consumption_phase();
  223.     flush_dead_units();
  224.     game_end_phase();
  225.     }
  226. }
  227.  
  228. /* Do random small things to get the turn started, including resetting */
  229. /* some side vars that could get things confused, if set wrong. */
  230. /* Most movement-related things should get set later, at beginning of */
  231. /* move phase. */
  232.  
  233. init_turn_phase()
  234. {
  235.     Side *side;
  236.  
  237.     ++global.time;
  238.     if (Debug) printf("##### TURN %d #####\n", global.time);
  239.     for_all_sides(side) {
  240.     wedge_mode(side);
  241.     show_timemode(side);
  242.     if (giventime > 0) update_clock(side);
  243.     flush_input(side);
  244.     flush_output(side);
  245.     }
  246. }
  247.  
  248. /* The win/lose phase assesses the sides' performance during that turn, */
  249. /* and can end the game.  In fact, it is the only way to get out, except */
  250. /* for the quick exit command.  With multiple mixed players, there are a */
  251. /* number of possibilities at game end, such as an all machine win.  Also, */
  252. /* it is possible for all players to lose simultaneously! */
  253.  
  254. game_end_phase()
  255. {
  256.     bool enemiesleft = FALSE;
  257.     int sidesleft = 0, testrslt;
  258.     Side *side, *side2, *survivor;
  259.  
  260.     if (Debug) printf("Entering game end phase\n");
  261.     /* See if any sides have won or lost */
  262.     if (global.time >= global.endtime) {
  263.     notify_all("The end of the world has arrived!");
  264.     for_all_sides(side) side_loses(side, NULL);
  265.     } else if (Build) {
  266.     /* No winning or losing while building */
  267.     } else {
  268.     for_all_sides(side) {
  269.         if (!side->lost) {
  270.         if (side->timedout) {
  271.             notify_all("The %s ran out of time!",
  272.                    plural_form(side->name));
  273.             side_loses(side, NULL);
  274.         } else if (all_units_gone(side)) {
  275.             notify_all("The %s have been wiped out!",
  276.                    plural_form(side->name));
  277.             side_loses(side, NULL);
  278.         } else {
  279.             testrslt = condition_true(side);
  280.             if (testrslt > 0) side_wins(side);
  281.             if (testrslt < 0) side_loses(side, NULL);
  282.         }
  283.         }
  284.     }
  285.     }
  286.     /* See if any opposing sides left */
  287.     for_all_sides(side) {
  288.     for_all_sides(side2) {
  289.         if (!side->lost && !side2->lost && enemy_side(side, side2))
  290.         enemiesleft = TRUE;
  291.     }
  292.     if (!side->lost) {
  293.         survivor = side;
  294.         sidesleft++;
  295.     }
  296.     }
  297.     /* Decide if the game is over */
  298.     if (numsides == 1) {
  299.     /* A one-player game can't end */
  300.     } else if (sidesleft == 0) {
  301.         exit_xconq();
  302.     } else if (!enemiesleft) {
  303.     winner = survivor;
  304.     if (sidesleft == 1) {
  305.         notify(winner, "You have prevailed over your enemies!");
  306.     } else {
  307.         for_all_sides(side) {
  308.         if (!side->lost) {
  309.             notify(side, "Your alliance has defeated its enemies!");
  310.         }
  311.         }
  312.     }
  313.     wait_to_exit(winner);
  314.     exit_xconq();
  315.     } else {
  316.         /* Game isn't over yet */
  317.     }
  318. }
  319.  
  320. /* Quicky test for absence of all units. */
  321.  
  322. all_units_gone(side)
  323. Side *side;
  324. {
  325.     int u;
  326.  
  327.     for_all_unit_types(u) if (side->units[u] > 0) return FALSE;
  328.     return TRUE;
  329. }
  330.  
  331. /* Check out all the winning and losing conditions, returning a flag */
  332. /* on which, if any, are true.  The three return values are 1 for win, */
  333. /* -1 for lose, and 0 for undecided. */
  334.  
  335. condition_true(side)
  336. Side *side;
  337. {
  338.     int i, u, r, amt = 0;
  339.     Unit *unit;
  340.     Condition *cond;
  341.  
  342.     for (i = 0; i < global.numconds; ++i) {
  343.     cond = &(global.winlose[i]);
  344.     if ((between(cond->starttime, global.time, cond->endtime)) &&
  345.         (cond->sidesn == -1 || cond->sidesn == side_number(side))) {
  346.         switch (cond->type) {
  347.         case TERRITORY:
  348.         for_all_unit_types(u) {
  349.             amt += side->units[u] * utypes[u].territory;
  350.         }
  351.         if (cond->win) {
  352.             if (amt >= cond->n) {
  353.             notify_all("The %s have enough territory to win!",
  354.                    plural_form(side->name));
  355.             return 1;
  356.             }
  357.         } else {
  358.             if (amt < cond->n) {
  359.             notify_all("The %s don't have enough territory!",
  360.                    plural_form(side->name));
  361.             return -1;
  362.             }
  363.         }
  364.         break;
  365.         case UNITCOUNT:
  366.         if (cond->win) {
  367.             for_all_unit_types(u) {
  368.             if (side->units[u] < cond->units[u]) return 0;
  369.             }
  370.             notify_all("The %s have enough units to win!",
  371.                    plural_form(side->name));
  372.             return 1;
  373.         } else {
  374.             for_all_unit_types(u) {
  375.             if (side->units[u] > cond->units[u]) return 0;
  376.             }
  377.             notify_all("The %s don't have the required units!",
  378.                    plural_form(side->name));
  379.             return -1;
  380.         }
  381.         break;
  382.         case RESOURCECOUNT:
  383.         if (cond->win) {
  384.             for_all_resource_types(r) {
  385.             if (side->resources[r] < cond->resources[r]) return 0;
  386.             }
  387.             notify_all("The %s have enough resources to win!",
  388.                    plural_form(side->name));
  389.             return 1;
  390.         } else {
  391.             for_all_resource_types(r) {
  392.             if (side->resources[r] > cond->resources[r]) return 0;
  393.             }
  394.             notify_all("The %s don't have the required resources!",
  395.                    plural_form(side->name));
  396.             return -1;
  397.         }
  398.         break;
  399.         case POSSESSION:
  400.         unit = unit_at(cond->x, cond->y);
  401.         if (cond->win) {
  402.             if (unit != NULL && unit->side == side) {
  403.             notify_all("The %s have got hex %d,%d!",
  404.                    plural_form(side->name), cond->x, cond->y);
  405.             return 1;
  406.             }
  407.         } else {
  408.             if (unit == NULL || unit->side != side) {
  409.             notify_all("The %s don't have hex %d,%d!",
  410.                    plural_form(side->name), cond->x, cond->y);
  411.             return -1;
  412.             }
  413.         }
  414.         break;
  415.         default:
  416.         case_panic("condition type", cond->type);
  417.         break;
  418.         }
  419.     }
  420.     }
  421.     return 0;
  422. }
  423.  
  424. /* Winning is defined as not losing :-) */
  425.  
  426. side_wins(side)
  427. Side *side;
  428. {
  429.     Side *side2;
  430.  
  431.     for_all_sides(side2) {
  432.     if (!allied_side(side, side2)) side_loses(side2, NULL);
  433.     }
  434. }
  435.  
  436. /* Be rude to losers and give all their units to their defeaters (might */
  437. /* not be one, so units become neutral or dead).  Give the loser a chance */
  438. /* to study the screen, mark the side as lost, then pass on this detail to */
  439. /* all the other sides and remove spurious leftover images of units. */
  440.  
  441. side_loses(side, victor)
  442. Side *side, *victor;
  443. {
  444.     int ux, uy;
  445.     Unit *unit;
  446.     Side *side2;
  447.  
  448.     notify(side, "You lost, %s!", (flip_coin() ? "sucker" : "turkey"));
  449.     if (active_display(side)) {
  450.     wait_to_exit(side);
  451.     close_display(side);
  452.     wedge_mode(side);
  453.     }
  454.     for_all_units(unit) {
  455.     if (alive(unit) && unit->side == side) {
  456.         ux = unit->x;  uy = unit->y;
  457.         unit_changes_side(unit, victor, CAPTURE, SURRENDER);
  458.         all_see_hex(ux, uy);
  459.     }
  460.     }
  461.     side->lost = TRUE;
  462.     for_all_sides(side2) {
  463.     if (active_display(side2)) {
  464.         remove_images(side2, side_number(side));
  465.         if (side != side2) show_all_sides(side2);
  466.     }
  467.     }
  468. }
  469.  
  470. /* Leave screen up so everybody can study it, and allow any input to take */
  471. /* it down.  OK to freeze all the other sides at once here. */
  472.  
  473. wait_to_exit(side)
  474. Side *side;
  475. {
  476.     if (humanside(side) && active_display(side)) {
  477.     sprintf(side->promptbuf, "[Do anything to exit]");
  478.     show_prompt(side);
  479.     freeze_wait(side);
  480.     }
  481. }
  482.  
  483. /* This routine should be called before any sort of non-death exit. */
  484. /* Among other things, it updates the statistics. */
  485.  
  486. exit_xconq()
  487. {
  488.     int n = 0;
  489.     Unit *unit;
  490.     Side *side;
  491.  
  492.     close_displays();
  493.     /* Need to kill off all units to finish up statistics */
  494.     for_all_units(unit) kill_unit(unit, (winner ? VICTOR : SURRENDER));
  495.     print_statistics();
  496.     /* Offer a one-line comment on the game and then leave */
  497.     for_all_sides(side) if (!side->lost) n++;
  498.     switch (n) {
  499.     case 0:
  500.     printf("\nEverybody lost!\n\n");
  501.     break;
  502.     case 1:
  503.     printf("\nThe %s (%s) won!\n\n",
  504.            plural_form(winner->name),
  505.            (winner->host ? winner->host : "machine"));
  506.     break;
  507.     default:
  508.     sprintf(spbuf, "");
  509.     for_all_sides(side) {
  510.         if (!side->lost) {
  511.         if (strlen(spbuf) > 0) {
  512.             sprintf(tmpbuf, "/");
  513.             strcat(spbuf, tmpbuf);
  514.         }
  515.         sprintf(tmpbuf, "%s", side->name);
  516.         strcat(spbuf, tmpbuf);
  517.         if (side->host) {
  518.             sprintf(tmpbuf, "(%s)", side->host);
  519.             strcat(spbuf, tmpbuf);
  520.         }
  521.         }
  522.     }
  523.     printf("\nThe %s alliance won!\n\n", spbuf);
  524.     break;
  525.     }
  526.     exit(0);
  527. }
  528.  
  529. /* Shut down displays - should be done before any sort of exit. */
  530.  
  531. close_displays()
  532. {
  533.     Side *side;
  534.  
  535.     for_all_sides(side) if (active_display(side)) close_display(side);
  536. }
  537.  
  538. /* This is to find out how everybody did. */
  539.  
  540. print_statistics()
  541. {
  542.     Side *side;
  543.     FILE *fp;
  544.  
  545. #ifdef STATISTICS
  546.     if ((fp = fopen(STATSFILE, "w")) != NULL) {
  547.     for_all_sides(side) {
  548.         print_side_results(fp, side);
  549.         print_unit_record(fp, side);
  550.         print_combat_results(fp, side);
  551.         if (side->next != NULL) fprintf(fp, "\f\n");
  552.     }
  553.     fclose(fp);
  554.     } else {
  555.     fprintf(stderr, "Can't open statistics file \"%s\"\n", STATSFILE);
  556.     }
  557. #endif /* STATISTICS */
  558. }
  559.  
  560. /* Read in a file in a special format - basically lines describing mapfiles */
  561. /* with markers for various types in between.  Returns true if the file */
  562. /* was actually found. */
  563.  
  564. read_menu_file()
  565. {
  566.     int i, j;
  567.     char *tmp;
  568.     FILE *fp;
  569.  
  570.     scnstart = scnend = perstart = perend = mapstart = mapend = 0;
  571.     make_pathname(XCONQLIB, MAPFILEFILE, NULL, spbuf);
  572.     if ((fp = fopen(spbuf, "r")) != NULL) {
  573.     for (i = 0; i < MAXMAPMENU; ++i) {
  574.         fscanf(fp, "%s", tmpbuf);
  575.         mapmenu[i].name = copy_string(tmpbuf);
  576.         tmp = read_line(fp);
  577.         for (j = 0; tmp[j] != '\0'; ++j) if (tmp[j] != ' ') break;
  578.         mapmenu[i].description = tmp + j;
  579.         if (strcmp(mapmenu[i].name, "Scenarios") == 0) {
  580.         scnstart = i+1;
  581.         } else if (strcmp(mapmenu[i].name, "Periods") == 0) {
  582.         scnend = i-1;
  583.         perstart = i+1;
  584.         } else if (strcmp(mapmenu[i].name, "Maps") == 0) { 
  585.         perend = i-1;
  586.         mapstart = i+1;
  587.         } else if (strcmp(mapmenu[i].name, "End") == 0) {
  588.         mapend = i-1;
  589.         break;
  590.         }
  591.     }
  592.     fclose(fp);
  593.     return TRUE;
  594.     } else {
  595.     return FALSE;
  596.     }
  597. }
  598.  
  599. /* Display menus of mapfiles.  Player can either choose a single scenario */
  600. /* or a period + map (but no guarantees that map will work with period!) */
  601.  
  602. ask_about_mapfiles()
  603. {
  604.     char *name;
  605.  
  606.     if (file_menu(scnstart, scnend, "scenario",
  607.           "Enter a number, or hit return to look at maps and periods.",
  608.           &name)) {
  609.     make_pathname(NULL, name, "scn", spbuf);
  610.     rawfilenames[numfiles++] = copy_string(spbuf);
  611.     } else {
  612.     if (file_menu(perstart, perend, "historical period",
  613.               "Enter a number, or hit return for the standard period.",
  614.               &name)) {
  615.         make_pathname(NULL, name, "per", spbuf);
  616.         rawfilenames[numfiles++] = copy_string(spbuf);
  617.     } else {
  618.         printf("\nYou will get the standard period.\n");
  619.     }    
  620.     if (file_menu(mapstart, mapend, "map",
  621.               "Enter a number, or hit return for a random map.",
  622.               &name)) {
  623.         make_pathname(NULL, name, "map", spbuf);
  624.         rawfilenames[numfiles++] = copy_string(spbuf);
  625.     } else {
  626.         printf("\nYou will get a random map.\n");
  627.     }
  628.     }
  629. }
  630.  
  631. /* Given an array of names, return success or failure of choice and maybe */
  632. /* one of the names. */
  633.  
  634. file_menu(start, end, prompt, help, rslt)
  635. int start, end;
  636. char *prompt, *help, **rslt;
  637. {
  638.     int i, ans;
  639.  
  640.     printf("\n");
  641.     if (start > end) return FALSE;
  642.     for (i = start; i <= end; ++i) {
  643.     printf("[%d]  %-14s  %s\n",
  644.            i - start + 1, mapmenu[i].name, mapmenu[i].description);
  645.     }
  646.     while (TRUE) {
  647.     printf("\nChoose a %s: ", prompt);
  648.     gets(spbuf);
  649.     sscanf(spbuf, "%d", &ans);
  650.     ans += start - 1;
  651.     if (ans >= start && ans <= end) {
  652.         *rslt = mapmenu[ans].name;
  653.         return TRUE;
  654.     } else if (iindex('?', spbuf) >= 0) {
  655.         printf("%s\n", help);
  656.     } else if (strlen(spbuf) == 0) {
  657.         return FALSE;
  658.     } else {
  659.         printf("Garbled answer!  Try again...\n");
  660.     }
  661.     }
  662. }
  663.  
  664. /* This allows fairly general setup for a collection of players.  There */
  665. /* should be facilities for removing as well as adding... */
  666.  
  667. ask_about_players()
  668. {
  669.     int i, n;
  670.  
  671.     while (TRUE) {
  672.     printf("\nPlayers so far: ");
  673.     list_players(stdout);
  674.     printf("\n\nAny others? [number or hostname]: ");
  675.     gets(spbuf);
  676.     if (strlen(spbuf) == 0) {
  677.         return;
  678.     } else if (strcmp(spbuf, "r") == 0) {
  679.         numgivens = 0;
  680.     } else if (iindex('?', spbuf) >= 0) {
  681.         printf("You can make various combinations of players,\n");
  682.         printf("by giving either a number of machine or a host name.\n");
  683.         printf("`r' clears the list; hit return when done.\n");
  684.     } else if (numgivens < MAXSIDES) {
  685.         if (sscanf(spbuf, "%d", &n) == 1) {
  686.         n = max(0, min(n, MAXSIDES - numgivens));
  687.         for (i = 0; i < n; ++i) add_player(FALSE, NULL);
  688.         } else {
  689.         add_player(TRUE, copy_string(spbuf));
  690.         }
  691.     } else {
  692.         printf("Can't add anybody else; 'r' starts over.\n");
  693.     }
  694.     }
  695. }
  696.  
  697. /* List all the specified players briefly. */
  698.  
  699. list_players(fp)
  700. FILE *fp;
  701. {
  702.     int i;
  703.  
  704.     if (numgivens > 0) {
  705.     fprintf(fp, "%s", (hosts[0] ? hosts[0] : "machine"));
  706.     for (i = 1; i < numgivens; ++i) {
  707.         fprintf(fp, ", %s", (hosts[i] ? hosts[i] : "machine"));
  708.     }
  709.     } else {
  710.     fprintf(fp, "no players defined.");
  711.     }
  712. }
  713.